﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using COFE.Core;
using COFE.Core.Utils;
using System.Xml.Linq;
using COFE.Core.Lister;
using COFE.Web;
using COFE.Web.Utils;
using System.Runtime.Serialization;
using System.Xml.Serialization;

namespace COFE.Web
{

    /// <summary>
    /// Entry that initialized using an IFileSystemInfo (Local).
    /// </summary>
    public class FSIEntry : Entry, ISupportLinkTemplates, IXmlSerializable
    {
        #region Constructor

        private void init(IFileSystemInfo entry)
        {
            base.ID = entry.GetId();
            base.EntryTypeID =
                (Guid)entry.InternalProperties
                .GetProperty(FileSystemInfoProperties.EntryTypeInfoKey).Value;

            if (entry.ParseName == ProfileRootDirectoryLister.ProfileRootParsablePath)
            {
                base.Name = "";
                base.Path = "";
            }
            else
            {
                base.Name = entry.Name;
                base.Path = entry.ParseName.Replace('\\', '/'); ;
            }

            if (entry is IFileInfo)
            {
                base.Length = (entry as IFileInfo).Length;
            }

            base.UtcTime = entry.LastWriteTime;
            base.BaseType = EntryTypeExtension.FromCOFE(entry);
            base.MetadataProperties = EnumUtils<MetadataAttribute>.FilterPropertiesForAttribute(
                       _entity.GetSupportedProperty());
        }

        internal FSIEntry(IFileSystemInfo entry)
            : base(entry.InternalProperties)
        {
            _entity = entry as IInternalFileSystemInfoExA;
            init(entry);
        }

        internal FSIEntry(IUserProfile profile, string path, EntryBaseType type, bool canConstruct)            
            : base()
        {
            IInternalFileSystemInfoExA outputEntry;

            var lookupEnty = profile.TryParsePath(path);            
            if (lookupEnty == null)
            {
                if (!canConstruct)
                    throw new System.IO.FileNotFoundException(path);

                //Construct it
                switch (type)
                {
                    case EntryBaseType.File:
                        using (profile.File.Create(path)) { }; break;
                    case EntryBaseType.Folder:
                        profile.Directory.CreateDirectory(path); break;
                    default: throw new NotSupportedException(type.ToString());
                }                

                //Try lookup again
                lookupEnty = profile.TryParsePath(path, Core.Profile.EntryConstructionFlags.DefaultWithLookup);
                if (lookupEnty == null)
                    throw new Exception("Entry construction failed. " + path);
                outputEntry = (IInternalFileSystemInfoExA)lookupEnty;
            }
            else outputEntry = (IInternalFileSystemInfoExA)lookupEnty;

            init(outputEntry);
            _entity = outputEntry;
        }

        /// <summary>
        /// This is for Serialization only!!!
        /// </summary>
        public FSIEntry()
            : base() { }


        /// <summary>
        /// Deserialize an Entry, 
        /// Metadata cannot be deserialized.
        /// </summary>
        /// <param name="manager"></param>
        /// <param name="itemXml"></param>
        /// <param name="canConstruct"></param>
        /// <returns></returns>
        public static Entry FromXml(IUserProfile profile, XElement itemXml, bool canConstruct)
        {
            EntryBaseType eType = EntryTypeExtension.Parse(itemXml.Attribute(XNames.EntryBaseType).Value);
            string path = itemXml.Element(XNames.Path).Value;
            var lookupEnty = profile.TryParsePath(path);
            if (lookupEnty != null)
                return new FSIEntry(lookupEnty);

            Guid id = new Guid(itemXml.Attribute(XNames.ID).Value);
            return new FSIEntry(profile, path, eType, canConstruct) { 
                ID = id };
        }

        #endregion

        #region Methods

        public override IEnumerable<Link> GetLinks()
        {
            return this.CreateLinks();
        }

        public override PropertyPair GetProperty(object property)
        {
            try
            {
                return _entity.GetProperty(property);
            }
            catch (Exception ex)
            { throw new ArgumentException("Get Property failed.", ex); }
        }

        public override PropertyPair GetProperty(string propertyName)
        {
            try
            {
                //Fail if the property is not Metadata or Resource, or not exists.
                return _entity.GetProperty(propertyName);
            }
            catch (Exception ex)
            { throw new ArgumentException("Get Property failed.", ex); }

        }

        public override System.IO.Stream OpenStream(bool write = false)
        {
            if (BaseType == EntryBaseType.File)
            {
                if (write)
                    return (_entity as IFileInfo).OpenWrite();
                return (_entity as IFileInfo).OpenRead();
            }
            throw new NotSupportedException();
        }

        public override void Refresh()
        {
            IFileSystemInfo entry = (IFileSystemInfo)_entity;
            entry.Refresh();
            init(entry);
        }

        /// <summary>
        /// Return a list of LinkTemplate s, LinkTemplates are look like "/#contents#/#id#".
        /// </summary>
        /// <returns></returns>
        public IEnumerable<LinkTemplate> GetLinkTemplates()
        {
            yield return new LinkTemplate(MediaType.Entry, RelationType.Self, ServiceConstants.Templates.Entry);
            yield return new LinkTemplate(MediaType.Meta, RelationType.Metadata, ServiceConstants.Templates.EntryMetadata);
            yield return new LinkTemplate(MediaType.EntryType, RelationType.TypeInfo, ServiceConstants.Templates.EntryEntryType);
            if (_entity is IDirectoryInfo)
            {
                yield return new LinkTemplate(MediaType.EntryList, RelationType.List, ServiceConstants.Templates.EntryList);
            }
            else if (_entity is IFileInfo)
            {
                yield return new LinkTemplate(MediaType.Stream, RelationType.Download, ServiceConstants.Templates.EntryStream);
            }

            foreach (var property in EnumUtils<ResourceAttribute>.FilterPropertiesForAttribute(_entity.GetSupportedProperty()))
            {
                ResourceAttribute ra = EnumUtils<ResourceAttribute>.FindAttribute(property);
                if (ra.MajorProperty)
                {
                    MediaType mediaType = ra.MediaType;
                    yield return new LinkTemplate(mediaType, property.ToString(),
                        ServiceConstants.Templates.Entry + "/" + EnumUtils.GetResourceName(property).ToLower());
                }
            }
        }

        public Dictionary<string, string> GetLinkParameterDic()
        {
            return new Dictionary<string, string>()
            {
                { "#id#", ID.ToString() },
                { "#typeid#", EntryTypeID.ToString() },
                { "#type#", BaseType.ToString() },
                { "#path#", Path.ToString() }
            };
        }


        public override System.Xml.Schema.XmlSchema GetSchema()
        {
            throw new NotImplementedException();
        }

        public override void ReadXml(System.Xml.XmlReader reader)
        {
            throw new NotSupportedException();
        }

        public override void WriteXml(System.Xml.XmlWriter writer)
        {
            ToXml().WriteTo(writer);
        }

        #endregion

        #region Data

        //private IInternalFileSystemInfoExA _entity;
        
        #endregion

        #region Public Properties

        #endregion




       
    }
}
